package colibri.lcdui;

import colibri.App;
import colibri.keyboard.T9;
import colibri.keyboard.Qwerty;
import colibri.keyboard.SureType;
import javax.microedition.lcdui.Font;
import javax.microedition.lcdui.Graphics;

/**
 *
 * @author Bayron Thiengo Quinelato
 */
public class TextField extends Item implements Runnable {
    public static final int ANY = 0;
    public static final int NUMERIC_INT = 1;
    public static final int NUMERIC_FLOAT = 2;
    public static final int ANY_PASSWORD = 3;
    public static final int NUMERIC_PASSWORD = 4;
    public static final int DATE = 5;
    public static final int TYPE_T9 = 0;
    public static final int TYPE_QWERTY = 1;
    public static final int TYPE_SURETYPE = 2;

    private boolean editable = true;
    private int maxChar;
    private int constrains;
    private int type;
    private int inputW;
    private int labelColor;

    private boolean capsLook = false;
    private String strAux, password;
    private char chAux;

    private StringBuffer currentStringBuffer;
    private String currentString = new String();

    private int lastPressedKey = Integer.MIN_VALUE;
    private char[] chars;
    private int currentKeyStep = 0;
    private long lastKeyTimestamp = 0;
    private long maxKeyDelay = 500L;
    private int caretIndex = 0;
    private int caretLeft = 0;
    private int spaceCareLeft = 2;
    private boolean caretBlinkOn = true;
    private long caretBlinkDelay = 500L;
    private long lastCaretBlink = 0;
    private boolean goToNextChar = true;

    public Font inputFont;
    public Font labelFont;
    private int inputFontHeight = 0;
    private int labelFontHeight = 0;
    
    public TextField(String label, int x, int y, int maxChar, int constrains) {
        super(x, y, 0, 0, Graphics.LEFT|Graphics.TOP);
        super.label = label;

        if (App.platform == App.PLATFORM_BLACKBERRY) {
            type = TYPE_QWERTY;
        } else {
            type = TYPE_T9;
        }
        
        inputFont = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_MEDIUM);
        inputFontHeight = inputFont.getHeight();

        if (constrains == DATE) {
            inputW = inputFont.stringWidth("00/00/0000") + 5;
            maxChar = 10;
        } else if (constrains == ANY) {
            inputW = maxChar * inputFont.charWidth('w') + 5;
        } else if (constrains == NUMERIC_INT || constrains == NUMERIC_FLOAT) {
            inputW = maxChar * inputFont.charWidth('0') + 5;
        } else if (constrains == ANY_PASSWORD || constrains == NUMERIC_PASSWORD) {
            inputW = (maxChar - 1) * inputFont.charWidth('*') + inputFont.charWidth('w') + 5;
            password = "";
        }

        super.width = inputW;
        super.height = inputFontHeight + 4;
        if (label != null && label.length() > 0) {
            labelFont = Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_MEDIUM);
            labelFontHeight = labelFont.getHeight();
            super.height += labelFontHeight;
            if (labelFont.stringWidth(label) > super.width) {
                super.width = labelFont.stringWidth(label);
            }
        }
        labelColor = 0x000000;

        this.maxChar = maxChar;
        this.constrains = constrains;

        caretLeft = super.x + spaceCareLeft;
        currentStringBuffer = new StringBuffer();

        new Thread(this).start();
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.inputW = width;
        this.width = inputW;
        if (label != null && label.length() > 0 && labelFont.stringWidth(label) > super.width) {
            super.width = labelFont.stringWidth(label);
        }
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public void setFocus(boolean focus) {
        this.focus = focus;
    }

    public boolean hasFocus() {
        return focus;
    }

    public void setType(int type) {
        if (type != TYPE_QWERTY && type != TYPE_SURETYPE && type != TYPE_T9) {
            throw new IllegalArgumentException("Colibri error: Falha na criação de TextField. O argumento 'type' é inválido: " + type);
        }
        this.type = type;
    }

    private boolean isCapsLockKey(int keyCode) {
        if (((type == TYPE_QWERTY || type == TYPE_SURETYPE) && keyCode == 35) ||
            (type == TYPE_T9 && keyCode == 42)) {
           return true;
        } else {
            return false;
        }
    }

    private boolean isClearKey(int keyCode) {
        switch (keyCode) {
            case 8:
                if (type == TYPE_QWERTY || type == TYPE_SURETYPE)
                    return true;
                else
                    return false;
            case -8:
                if (type == TYPE_T9)
                    return true;
                else
                    return false;
            default:
                if (App.isKey_CLEAR(keyCode) && type != TYPE_QWERTY && type != TYPE_SURETYPE)
                   return true;
                else
                   return false;
        }
    }

    private void clearChar() {
        if (currentStringBuffer.length() > 0 && caretIndex > 0) {
            caretIndex--;
            currentStringBuffer.deleteCharAt(caretIndex);
            currentString = currentStringBuffer.toString();
        }
    }

    private void updateCaretPosition() {
        if (constrains == ANY_PASSWORD || constrains == NUMERIC_PASSWORD) {
            int tmpW = caretIndex * inputFont.stringWidth("*");
            caretLeft = super.x + spaceCareLeft + tmpW;
        } else {
            caretLeft = super.x + spaceCareLeft + inputFont.substringWidth(currentString, 0, caretIndex);
        }
    }

    public final void setPosition(int x, int y) {
        super.x = x;
        super.y = y;
        updateCaretPosition();
    }

    public int getConstrains() {
        return constrains;
    }

    public int getMaxChar() {
        return maxChar;
    }

    private void checkTimeStamps() {
        long currentTime = System.currentTimeMillis();
        if (lastCaretBlink + caretBlinkDelay < currentTime) {
            caretBlinkOn = !caretBlinkOn;
            lastCaretBlink = currentTime;
        }
        if (!goToNextChar && lastKeyTimestamp + maxKeyDelay < currentTime) {
            goToNextChar = true;
        }
    }

    public final void run() {
        try {
            while (true) {
                if (visible && focus && getCanvas() != null && getCanvas().isShown()) {
                    checkTimeStamps();
                    repaint();
                }
                synchronized (this) {
                    wait(50L);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new IndexOutOfBoundsException("Colibri error: Falha durante execução do Item TextFiel.");
        }
    }

    protected final void callPaint(Graphics g) {
        if (label != null && label.length() > 0) {
            g.setColor(labelColor);
            g.setFont(labelFont);
            g.drawString(label, super.x + super.width/2, super.y, 17);
        }
        if (editable) {
            g.setColor(0xFFFFFF);
        } else {
            g.setColor(0xCCCCCC);
        }
        g.fillRect(super.x + 1 + (width - inputW) / 2, super.y + labelFontHeight + 1, inputW - 2, super.height - labelFontHeight - 2);
        g.setColor(0);
        g.drawRect(super.x + (width - inputW) / 2, super.y + labelFontHeight, inputW - 1, super.height - labelFontHeight - 1);
        g.setFont(inputFont);
        if ((constrains == ANY_PASSWORD || constrains == NUMERIC_PASSWORD) && currentString.length() > 0) {
            password = "";
            for (int i = 0; i < currentString.length(); i++) {
                if (i == caretIndex-1 && !goToNextChar) {
                    password += currentString.charAt(i);
                } else {
                    password += "*";
                }
            }
            g.drawString(password, super.x + spaceCareLeft + (width - inputW) / 2, super.y + labelFontHeight + 2, super.anchor);
        } else {
            g.drawString(currentString, super.x + spaceCareLeft + (width - inputW) / 2, super.y + labelFontHeight + 2, super.anchor);
        }
        // Desenha o marcador de texto
        if (focus) {
            if (caretBlinkOn && goToNextChar) {
                g.drawLine(caretLeft + (width - inputW) / 2, super.y + labelFontHeight + 2, caretLeft + (width - inputW) / 2, super.y + labelFontHeight + 1 + inputFontHeight);
            }
        }
    }

    public final void setText(String text) {
        if (text == null) {
            if (constrains == NUMERIC_INT || constrains == NUMERIC_FLOAT) {
                text = "0";
            } else {
                text = "";
            }
        } else if (text.equals("") && (constrains == NUMERIC_INT || constrains == NUMERIC_FLOAT)) {
            text = "0";
        }
        if (text.length() > maxChar) {
            currentStringBuffer = new StringBuffer(text.substring(0, maxChar));
        } else {
            currentStringBuffer = new StringBuffer(text);
        }
        currentString = currentStringBuffer.toString();
        caretIndex = currentString.length();
        updateCaretPosition();
    }

    public final String getText() {
        if (currentString == null || currentString.length() <= 0) {
            if (constrains == NUMERIC_INT || constrains == NUMERIC_FLOAT) {
                return "0";
            } else {
                return "";
            }
        }
        return currentString;
    }

    public final String getPassword() {
        if (password == null || password.length() <= 0) {
            return "";
        }
        return password;
    }

    public final int size() {
        return currentString.length();
    }

    public void setLabelColor(int labelColor) {
        this.labelColor = labelColor;
    }

    protected final void callKeyReleased(int keyCode) {
        if (editable) {
            int gameAction = App.getGameAction(keyCode);
            if (isClearKey(keyCode)) {
                clearChar();
                updateCaretPosition();
                goToNextChar = true;
            } else if((constrains == ANY || constrains == ANY_PASSWORD) && isCapsLockKey(keyCode)) {
                if (capsLook)
                    capsLook = false;
                else
                    capsLook = true;
            } else if (keyCode >= 0 && keyCode <= 255) {
                switch(constrains) {
                    case ANY:
                    case ANY_PASSWORD:
                        writeAny(keyCode);
                        break;
                    case NUMERIC_INT:
                    case NUMERIC_PASSWORD:
                        writeNumeric_Int(keyCode);
                        break;
                    case NUMERIC_FLOAT:
                        writeNumeric_Float(keyCode);
                        break;
                    case DATE:
                        writeDate(keyCode);
                        break;
                }
            } else if (gameAction == App.LEFT) {
                if (caretIndex > 0) {
                    caretIndex--;
                    updateCaretPosition();
                    goToNextChar = true;
                }
            } else if (gameAction == App.RIGHT) {
                if (caretIndex < currentStringBuffer.length()) {
                    if (goToNextChar) {
                        caretIndex++;
                    }
                    updateCaretPosition();
                    goToNextChar = true;
                }
            } else {
                capsLook = false;
            }
        }
    }

    protected final void callKeyRepeated(int keyCode) {
        if (editable) {
            int gameAction = App.getGameAction(keyCode);
            if (isClearKey(keyCode)) {
                clearChar();
                updateCaretPosition();
                goToNextChar = true;
            } else if (gameAction == App.LEFT) {
                if (caretIndex > 0) {
                    caretIndex--;
                    updateCaretPosition();
                    goToNextChar = true;
                }
            } else if (gameAction == App.RIGHT) {
                if (caretIndex < currentStringBuffer.length()) {
                    if (goToNextChar) {
                        caretIndex++;
                    }
                    updateCaretPosition();
                    goToNextChar = true;
                }
            }
        }
    }

    private void writeAny(int keyCode) {
        if (goToNextChar || keyCode != lastPressedKey) {
            goToNextChar = true;
            lastPressedKey = keyCode;
            currentKeyStep = 0;
        } else {
            currentKeyStep++;
        }
        switch(type) {
            case TYPE_T9:
                if (currentStringBuffer.length() < maxChar || (currentStringBuffer.length() == maxChar && !goToNextChar && keyCode == lastPressedKey)) {
                    chars = T9.getCharacter(keyCode);
                    if (chars != null) {
                        if (currentKeyStep >= chars.length) {
                            currentKeyStep -= chars.length;
                        }
                        strAux = String.valueOf(chars[currentKeyStep]);
                        if (capsLook) {
                            strAux = strAux.toUpperCase();
                        }
                        chAux = strAux.charAt(0);
                        if (goToNextChar) {
                            currentStringBuffer.insert(caretIndex, chAux);
                            caretIndex++;
                        } else {
                            currentStringBuffer.setCharAt(caretIndex - 1, chAux);
                        }
                        currentString = currentStringBuffer.toString();
                        updateCaretPosition();
                        lastKeyTimestamp = System.currentTimeMillis();
                        goToNextChar = false;
                    }
                }
                break;

            case TYPE_QWERTY:
                if (currentStringBuffer.length() < maxChar) {
                    char ch = Qwerty.getCharacter(keyCode);
                    if (capsLook) {
                        strAux = String.valueOf(ch);
                        strAux = strAux.toUpperCase();
                        ch = strAux.charAt(0);
                    }
                    currentStringBuffer.insert(caretIndex, ch);
                    caretIndex++;
                    currentString = currentStringBuffer.toString();
                    updateCaretPosition();
                    lastKeyTimestamp = System.currentTimeMillis();
                    goToNextChar = false;
                }
                break;

            case TYPE_SURETYPE:
                if (currentStringBuffer.length() < maxChar || (currentStringBuffer.length() == maxChar && !goToNextChar && keyCode == lastPressedKey)) {
                    chars = SureType.getCharacter(keyCode);
                    if (chars != null) {
                        if (currentKeyStep >= chars.length) {
                            currentKeyStep -= chars.length;
                        }
                        strAux = String.valueOf(chars[currentKeyStep]);
                        if (capsLook) {
                            strAux = strAux.toUpperCase();
                        }
                        chAux = strAux.charAt(0);
                        if (goToNextChar) {
                            currentStringBuffer.insert(caretIndex, chAux);
                            caretIndex++;
                        } else {
                            currentStringBuffer.setCharAt(caretIndex - 1, chAux);
                        }
                        currentString = currentStringBuffer.toString();
                        updateCaretPosition();
                        lastKeyTimestamp = System.currentTimeMillis();
                        goToNextChar = false;
                    }
                }
                break;

            default:
                break;
        }
    }

    private void writeNumeric_Int(int keyCode) {
        if (currentStringBuffer.length() < maxChar) {
            if (goToNextChar || keyCode != lastPressedKey) {
                goToNextChar = true;
                lastPressedKey = keyCode;
                currentKeyStep = 0;
            } else {
                currentKeyStep++;
            }
            switch (type) {
                case TYPE_T9:
                    chAux = T9.getNumericInteger(keyCode);
                    break;
                case TYPE_QWERTY:
                    chAux = Qwerty.getNumericInteger(keyCode);
                    break;
                case TYPE_SURETYPE:
                    chAux = SureType.getNumericInteger(keyCode);
                    break;
                default:
                    break;
            }
            if (chAux != 'X') {
                currentStringBuffer.insert(caretIndex, chAux);
                caretIndex++;
                currentString = currentStringBuffer.toString();
                updateCaretPosition();
                lastKeyTimestamp = System.currentTimeMillis();
                goToNextChar = false;
            }
        }
    }

    private void writeNumeric_Float(int keyCode) {
        if (currentStringBuffer.length() < maxChar) {
            if (goToNextChar || keyCode != lastPressedKey) {
                goToNextChar = true;
                lastPressedKey = keyCode;
                currentKeyStep = 0;
            } else {
                currentKeyStep++;
            }
            switch (type) {
                case TYPE_T9:
                    chAux = T9.getNumericFloat(keyCode);
                    break;
                case TYPE_QWERTY:
                    chAux = Qwerty.getNumericInteger(keyCode);
                    break;
                case TYPE_SURETYPE:
                    chAux = SureType.getNumericInteger(keyCode);
                    break;
                default:
                    break;
            }
            if (chAux != 'X') {
                if (chAux == '.') {
                    if (caretIndex > 0 &&
                        ((currentStringBuffer.charAt(0) == '-' && caretIndex > 1) ||
                        (currentStringBuffer.charAt(0) != '-' && caretIndex == 1)) &&
                        currentStringBuffer.toString().indexOf(".") == -1) {
                        currentStringBuffer.insert(caretIndex, chAux);
                        caretIndex++;
                        currentString = currentStringBuffer.toString();
                        updateCaretPosition();
                        lastKeyTimestamp = System.currentTimeMillis();
                        goToNextChar = false;
                    }
                } else if (chAux == '-') {
                    if (caretIndex == 0 && currentStringBuffer.toString().indexOf("-") == -1) {
                        currentStringBuffer.insert(caretIndex, chAux);
                        caretIndex++;
                        currentString = currentStringBuffer.toString();
                        updateCaretPosition();
                        lastKeyTimestamp = System.currentTimeMillis();
                        goToNextChar = false;
                    }
                } else {
                    currentStringBuffer.insert(caretIndex, chAux);
                    caretIndex++;
                    currentString = currentStringBuffer.toString();
                    updateCaretPosition();
                    lastKeyTimestamp = System.currentTimeMillis();
                    goToNextChar = false;
                }
            }
        }
    }

    private void writeDate(int keyCode) {
        if (goToNextChar || keyCode != lastPressedKey) {
            goToNextChar = true;
            lastPressedKey = keyCode;
            currentKeyStep = 0;
        } else {
            currentKeyStep++;
        }

        if (currentStringBuffer.length() < maxChar || (currentStringBuffer.length() == maxChar && !goToNextChar && keyCode == lastPressedKey)) {
            if (keyCode >= 48 && keyCode <= 57) {
                currentStringBuffer.insert(caretIndex, (char)keyCode);
                caretIndex++;
            }
            String tmp = maskDate(currentStringBuffer.toString(), keyCode);
            if (tmp != null) {
                currentStringBuffer.delete(0, currentStringBuffer.length());
                currentStringBuffer.append(tmp);
                currentString = currentStringBuffer.toString();
                updateCaretPosition();
                lastKeyTimestamp = System.currentTimeMillis();
                goToNextChar = false;
            }
        }
    }

    private String maskDate (String inputData, int keyCode) {
        if (keyCode >= 47 && keyCode < 58) { // numeros de 0 a 9 e "/"
            String tmp = inputData;
            if ((tmp.length() == 2 || tmp.length() == 5) || ((caretIndex == 2 || caretIndex == 5) && tmp.charAt(caretIndex + 1) != '/' && tmp.charAt(caretIndex - 1) != '/')) {
                tmp += '/';
                inputData = tmp;
                caretIndex++;
            }
        }
        return inputData;
    }
}